import { contextBridge, ipcRenderer, shell } from 'electron';
import { electronAPI } from '@electron-toolkit/preload';
import { domReady } from './utils';
import { useLoading } from './loading';
const ffmpeg = require('fluent-ffmpeg');
const fs = require('fs');
const path = require('path');
const os = require('os');
const { PassThrough } = require('stream');

const getBinPath = () => {
  const platform = os.platform();
  const arch = os.arch();
  const basePath = path.resolve(
    __dirname.replace('app.asar', 'app.asar.unpacked'),
    'bin',
    platform,
    // arm64 is limit supported only for macOS
    platform === 'darwin' && arch === 'arm64' ? 'arm64' : 'x64',
  );
  var name = 'ffmpeg';
  var binPath = path.resolve(basePath, platform === 'win32' ? `${name}.exe` : name);
  var resPath = binPath.replace('\\out', '\\resources');
  return resPath.replace('\\preload', '\\out');
};

const { appendLoading, removeLoading } = useLoading();
// @ts-ignore (define in dts)
window.removeLoading = removeLoading;

domReady().then(appendLoading);

var ffmpegCommandArr: Array<any> = new Array<any>();
// Custom APIs for renderer
const api = {
  jsonToCsv: (jsonList, targetPath, fileName) => {
    // 这样我们得到了一个第一个为表头，剩余为内容的数组了
    var csvDataList = jsonList.reduce((prev, current, index) => {
      // 因为每个 json 对象的 key 都是相同的，因此我们只需要取一个就行
      if (index === 0) {
        prev.push(Object.keys(current));
      }
      // 将每个值都存到一个数组中去
      var dataList = [];
      for (var key in current) {
        dataList.push(current[key]);
      }
      prev.push(dataList);
      return prev;
    }, []);
    // 将上一步得到的数组转为字符串
    var writeData = csvDataList
      .map((data) => {
        // 每个数组通过逗号连接 map 返回一个数组
        return data.join(',');
      })
      .join('\r\n'); // 对返回的大数组再次用换行符拼接
    return new Promise((resolve, reject) => {
      // 在教程中为了与源文件区别，所以在头上特意加上一下生成的时间
      // 如果需要拿去做正式使用，下面这段可以去除
      // writeData = `${new Date().toLocaleString()}\r\n` + writeData
      // 为了对付 excel 打开乱码，在头上要加上 BOM 头
      // writeData = new Buffer('\xEF\xBB\xBF', 'binary') + writeData
      fs.writeFile(`${targetPath}/${fileName}`, writeData, 'utf-8', (err) => {
        if (err) {
          reject(err);
        }
        resolve();
      });
    });
  },

  openBrowser: (url) => {
    shell.openExternal(url);
  },

  openPath: (url) => {
    shell.openPath(url);
  },

  openFile: (extArr) => ipcRenderer.invoke('dialog:openFile', extArr),

  openDirectory: () => ipcRenderer.invoke('dialog:openDirectory'),

  getFilesFromDir: (inputPath, extArr) => {
    let fileData: Array<any> = new Array<any>();
    const readImgFilePath = (directoryPath) => {
      directoryPath = path.resolve(directoryPath);
      const files = fs.readdirSync(directoryPath);
      files.forEach((file) => {
        const filePath = path.join(directoryPath, file);
        const stat = fs.statSync(filePath);
        if (stat.isFile()) {
          var extname = path.extname(file).toLocaleLowerCase();
          if (extname != null && extname != '') {
            extname = extname.substring(1);
            if (extArr.includes(extname)) {
              fileData.push({
                filePath: filePath,
                fileSize: stat.size,
              });
            }
          }
        } else if (stat.isDirectory()) {
          readImgFilePath(filePath);
        }
      });
    };
    readImgFilePath(inputPath);
    return fileData;
  },

  getFileSize: (filePath) => {
    filePath = path.resolve(filePath);
    const stat = fs.statSync(filePath);
    if (stat.isFile()) {
      return stat.size;
    } else {
      return null;
    }
  },

  fileExists: (filePath) => {
    return fs.existsSync(path.resolve(filePath));
  },

  removeFile: (filePath) => {
    try {
      if (fs.existsSync(path.resolve(filePath))) {
        fs.unlinkSync(filePath);
      }
      return true;
    } catch (e) {
      return false;
    }
  },

  getVideoOrAudioMetaData: async (input, callback) => {
    await ffmpeg.setFfmpegPath(getBinPath());
    ffmpeg(input).ffprobe((err, data) => {
      if (err == null && callback != null) {
        callback(data);
      }
    });
  },

  getVideoOrAudioMetaDataPromise: async (input) => {
    await ffmpeg.setFfmpegPath(getBinPath());
    return new Promise((resolve, reject) => {
      ffmpeg(input).ffprobe((err, data) => {
        if (err == null) {
          resolve(data);
        }
      });
    });
  },

  execFfmpegPromise: async (inputPath, outputPath, progressCallback) => {
    ffmpeg.setFfmpegPath(getBinPath());
    return new Promise((resolve, reject) => {
      ffmpeg(inputPath)
        .output(outputPath)
        .on('error', (err) => {
          reject(err);
        })
        .on('progress', function (progress) {
          if (progressCallback != null) {
            progressCallback(progress);
          }
        })
        .on('end', () => {
          resolve(outputPath);
        })
        .run();
    });
  },

  execFfmpeg: (input, output, opts, progressCallback, endCallback, errorCallback) => {
    try {
      ffmpeg.setFfmpegPath(getBinPath());
      let ffmpegCommand = ffmpeg(input)
        .on('start', function (commandLine) {
          console.log('Format conversion start: ' + commandLine);
        })
        .on('progress', function (progress) {
          //console.log('Processing: ' + progress.percent + '% done');
          if (progressCallback != null) {
            progressCallback(progress);
          }
        })
        .on('end', function (stdout, stderr) {
          console.log('Format conversion succeeded!');
          if (endCallback != null) {
            endCallback();
          }
        })
        .on('error', function (err, stdout, stderr) {
          console.log('Cannot process: ', err);
          if (errorCallback != null) {
            errorCallback();
          }
        })
        .save(output);
      ffmpegCommandArr.push(ffmpegCommand);
    } catch (e) {
      console.log(e);
      if (errorCallback != null) {
        errorCallback();
      }
    }
  },

  killFfmpegCommand: () => {
    for (var i = ffmpegCommandArr.length - 1; i >= 0; i--) {
      ffmpegCommandArr[i].kill();
      ffmpegCommandArr.splice(i, 1);
    }
  },

  //文件拷贝，并计算拷贝进度
  copyFileWithProgress: (source, destination, callback) => {
    const total = fs.statSync(source).size;
    let transferred = 0;

    const r = fs.createReadStream(source);
    const p = new PassThrough();
    const w = fs.createWriteStream(destination);

    r.pipe(p);
    p.pipe(w);

    r.on('data', (chunk) => {
      transferred += chunk.length;
      callback(transferred, total);
    });

    w.on('close', () => {
      transferred = total;
      callback(total, total);
    });

    w.on('error', (err) => {
      callback(err);
    });

    //取消文件拷贝
    return () => {
      r.unpipe(w);
      r.destroy();
      w.end();
    };
  },
};

// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
  try {
    contextBridge.exposeInMainWorld('electron', electronAPI);
    contextBridge.exposeInMainWorld('api', api);
  } catch (error) {
    console.error(error);
  }
} else {
  // @ts-ignore (define in dts)
  window.electron = electronAPI;
  // @ts-ignore (define in dts)
  window.api = api;
}
